﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Security.Permissions;
using System.Threading;
using System.Configuration;

namespace FolderSync
{
    public class Watcher
    {
        private ConfigurationSet[] _configSets;

        public ConfigurationSet[] ConfigSets
        {
            get { return _configSets; }
            set { _configSets = value; }
        }
        public INotifier Notifier { get; set; }

        Syncer syncer = null;

        public Syncer Syncer
        {
            get { return syncer; }
        }
        MirrorSerializer _mirrorSerializer = null;
        public MirrorSerializer MirrorSerializer
        {
            get { return _mirrorSerializer; }
        }

        public delegate void StatusChangedHandler(object sender, string status);
        public event StatusChangedHandler StatusChanged;
        void syncer_StatusChanged(Syncer sender, string status)
        {
            if (StatusChanged != null) StatusChanged(sender, status);
        }
        void _mirrorSerializer_StatusChanged(object sender, string status)
        {
            if (StatusChanged != null) StatusChanged(sender, status);
        }
        private bool _noRun = true;

        public bool NoRun
        {
            get { return _noRun; }
            set { _noRun = value; }
        }
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        public void Run()
        {
            syncer = new Syncer()
            {
                Notifier = Notifier,
            };
            syncer.StatusChanged += new FolderSync.Syncer.StatusChangedHandler(syncer_StatusChanged);
            // wake the Syncer in his own thread
            var syncThread = new Thread(syncer.Run);
            syncThread.Name = "Syncer";
            if (!_noRun)
            {
                syncThread.Start();
                Notifier.Message("Syncer thread started");
            }

            // Begin watching.
            // Create a new FileSystemWatcher and set its properties.
            _mirrorSerializer = new MirrorSerializer() { configsets = _configSets, Notifier = Notifier, Syncer = syncer };
            _mirrorSerializer.StatusChanged+=new FolderSync.MirrorSerializer.StatusChangedHandler(_mirrorSerializer_StatusChanged);
            // start add and mirror
            var mirrorThread = new Thread(_mirrorSerializer.Run);
            mirrorThread.Name = "MirrorSerializer";
            if (!_noRun)
            {
                mirrorThread.Start();
                Notifier.Message("MirrorSerialyser thread started");
            }

            var watchers = new List<FileSystemWatcherTranslater>();
            try
            {
                foreach (var configSet in _configSets)
                {
                    var watcher = new FileSystemWatcherTranslater(configSet.Source);
                    watchers.Add(watcher);
                    Notifier.Message("Watcher starting, watching path «{0}» and subfolders", watcher.Path);
                    if (configSet.Destination != string.Empty)
                        Notifier.Message("Synchronizing «{0}» with «{1}» and subfolders", watcher.Path, configSet.Destination);

                    watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
                    watcher.IncludeSubdirectories = true;
                    var source = configSet.Source;
                    var destination = configSet.Destination;
                    watcher.TranslatePath = (s => s.Replace(source, destination));
                    var enqueuer = new Enqueuer()
                    {
                        WatchOnly = string.IsNullOrEmpty(configSet.Destination),
                        Notifier = Notifier,
                        LogTag = configSet.Name,
                        filter = new RegExpFileFilter()
                        {
                            rd = configSet.IncludeDirectory,
                            rf = configSet.IncludeFile,
                            xd = configSet.ExcludeDirectory,
                            xf = configSet.ExcludeFile,
                        },
                        Syncer = syncer,
                    };

                    // Add event handlers.
                    watcher.Changed += new FileSystemEventHandler(enqueuer.OnChanged);
                    watcher.Created += new FileSystemEventHandler(enqueuer.OnChanged);
                    watcher.Deleted += new FileSystemEventHandler(enqueuer.OnChanged);
                    watcher.Renamed += new RenamedEventHandler(enqueuer.OnRenamed);

                    watcher.EnableRaisingEvents = true;

                    Notifier.Message("Watcher for «{0}» enabled", watcher.Path);
                }
                _running = true;
                var timestart = DateTime.Now;
                while (_running)
                {
                    if ((DateTime.Now - timestart).TotalSeconds > 60)
                    {
                        Notifier.Message(Syncer.Statistics.ToString(verbose: true));
                        Notifier.Message(Syncer.Status);
                        timestart = DateTime.Now;
                    }
                    Thread.Sleep(50);
                }
            }
            catch { }
            finally
            {
                foreach (var watcher in watchers)
                {
                    watcher.EnableRaisingEvents = false;
                    Notifier.Message("Watcher «{0}» disabled", watcher.Path);
                    watcher.Dispose();
                }
                syncer.Stop();
                Notifier.Message("Syncer thread stop requested");
                Notifier.Message(Syncer.Status);
                if (_mirrorSerializer != null)
                {
                    _mirrorSerializer.Stop();
                    Notifier.Message("Mirrorer thread stop requested");
                    if (mirrorThread.IsAlive) mirrorThread.Join();
                    Notifier.Message("Watcher: Mirrorer thread stopped");
                }
                if (syncThread.IsAlive) syncThread.Join();
                Notifier.Message("Watcher: Syncer thread stopped");
                Notifier.Message(Syncer.Statistics.ToString(verbose: true));
                Notifier.Message("Watcher Normal end");
            }
        }

        public void Stop()
        {
            _running = false;
        }

        private bool _running = false;
        public bool running { get { return _running; } }

    }
}
